//
// Copyright (c) 2024 BPer
//
#include <bperrtc/BPerPeerConnection.h>
#include <bperrtc/BPerRtcConnection.h>
#include <bperrtc/BPerStreamUrl.h>

#include <bperutil/sys/BPerLog.h>

#include <bperavutil/video/BPerMeta.h>

#include <bperzlm/BPerZlmConnection.h>
#include <bpersrs/BPerSrsConnection.h>
#include <bperp2p/BPerP2pConnection.h>
#include <bperwhip/BPerWhip.h>

static void bPer_pc_init(BPerPeer* peer){
	if(peer==NULL || peer->conn!=NULL) return;
	if (peer->conn == NULL)		peer->conn = bPer_calloc(1, sizeof(BPerRtcConnection));
	BPerRtcConnection *conn = (BPerRtcConnection*)peer->conn;
	bPer_create_rtcConnection(conn, &peer->streamconfig,peer->avinfo);
}
static int32_t bPer_pc_isConnected(BPerPeer* peer){
	if(peer==NULL||peer->conn==NULL) return BPer_Ok;
	BPerRtcConnection *conn = (BPerRtcConnection*) peer->conn;
	return conn->isConnected(conn->session);
}

 int32_t bPer_pc_initParam(char* url,BPerStreamConfig* stream,BPerAVInfo* avinfo,BPerRtcDirection opt){
	return bPer_stream_parseUrl(url, stream, avinfo, opt);
}

 int32_t bPer_p2p_getHttpSdp(BPerIpFamilyType family,char* httpIp,int32_t httpPort,char* localSdp,char* remoteSdp){
	return bPer_p2p_connectRtcServer(family,httpIp,httpPort,localSdp,remoteSdp);
}

 static int32_t bPer_pc_addAudioTrack(BPerPeer* peer,BPerAudioCodec codec){
 	if(peer==NULL||peer->conn==NULL) return ERROR_RTC_PEERCONNECTION;
 		BPerRtcConnection *conn = (BPerRtcConnection*) peer->conn;
 		return conn->addAudioTrack(conn->session,codec);

 }

 static int32_t bPer_pc_addVideoTrack(BPerPeer* peer,BPerVideoCodec codec){
 	if(peer==NULL||peer->conn==NULL) return ERROR_RTC_PEERCONNECTION;
 	BPerRtcConnection *conn = (BPerRtcConnection*) peer->conn;
 	return conn->addVideoTrack(conn->session,codec);
 }

 static int32_t bPer_pc_addTransceiver(BPerPeer* peer,BPerRtcDirection direction){
 	if(peer==NULL||peer->conn==NULL) return ERROR_RTC_PEERCONNECTION;
 	BPerRtcConnection *conn = (BPerRtcConnection*) peer->conn;
 	return conn->addTransceiver(conn->session,direction);
 }
static int32_t bPer_pc_connectServer(BPerPeer* peer){
	if(peer==NULL||peer->conn==NULL) return  ERROR_RTC_PEERCONNECTION;
	int32_t mediaServer=peer->avinfo->sys.mediaServer;
	BPerRtcConnection *conn = (BPerRtcConnection*)peer->conn;
	if(conn->isConnected(conn->session)) return BPer_Ok;

	if(mediaServer==BPer_Server_Zlm) return bPer_zlm_connectRtcServer(conn);

	return bPer_srs_connectRtcServer(conn);
}
static int32_t bPer_createDataChannel(BPerPeer* peer){
	if(peer==NULL||peer->conn==NULL) return ERROR_RTC_PEERCONNECTION;
	BPerRtcConnection *conn = (BPerRtcConnection*) peer->conn;
	return conn->createDataChannel(conn->session);
}

static int32_t bPer_pc_connectWhipWhepServer(BPerPeer* peer,char* url){
	if(peer==NULL||url==NULL) return ERROR_RTC_PEERCONNECTION;
	BPerRtcConnection *conn = (BPerRtcConnection*) peer->conn;
	return bPer_whip_connectPeer(conn,url);
}

static int32_t bPer_pc_stopRtc(BPerPeer* peer){
	if(peer==NULL||peer->conn==NULL) return  ERROR_RTC_PEERCONNECTION;
	BPerRtcConnection *conn = (BPerRtcConnection*) peer->conn;
	if(conn->session->context.state==BPer_Conn_State_Disconnected || conn->session->context.state==BPer_Conn_State_Closed)
		return BPer_Ok;
	conn->session->context.state = BPer_Conn_State_Disconnected;
	if(conn->onConnectionStateChange) conn->onConnectionStateChange(conn->session,BPer_Conn_State_Disconnected);
	bPer_trace("\nwebrtc disconnected\n");
	conn->close(conn->session);
	bPer_destroy_rtcConnection(conn);
	bPer_free(peer->conn);
	return BPer_Ok;
}

static int32_t bPer_pc_setRemoteDescription(BPerPeer* peer,char* sdp){
	if(peer==NULL||peer->conn==NULL) return  ERROR_RTC_PEERCONNECTION;
	BPerRtcConnection *conn = (BPerRtcConnection*) peer->conn;
	return conn->setRemoteDescription(conn->session,sdp);
}

static int32_t bPer_pc_setLocalDescription(BPerPeer* peer,char* sdp){
	if(peer==NULL||peer->conn==NULL) return  ERROR_RTC_PEERCONNECTION;
	BPerRtcConnection *conn = (BPerRtcConnection*) peer->conn;
	return conn->setLocalDescription(conn->session,sdp);
}

static int32_t bPer_pc_onVideo(BPerPeer* peer,BPerFrame* videoFrame){
	if(peer==NULL||peer->conn==NULL||videoFrame==NULL) return  ERROR_RTC_PEERCONNECTION;
	BPerRtcConnection *session = (BPerRtcConnection*) peer->conn;
	return  session->on_video(session->session,videoFrame);
}

static int32_t bPer_pc_onAudio(BPerPeer* peer,BPerFrame *audioFrame){
	if(peer==NULL||peer->conn==NULL||audioFrame==NULL) return  ERROR_RTC_PEERCONNECTION;
	BPerRtcConnection *conn = (BPerRtcConnection*) peer->conn;
	return  conn->on_audio(conn->session,audioFrame);
}

static int32_t bPer_pc_onMessage(BPerPeer* peer,BPerFrame *msgFrame){
	if(peer==NULL||peer->conn==NULL||msgFrame==NULL) return  ERROR_RTC_PEERCONNECTION;
	BPerRtcConnection *conn = (BPerRtcConnection*) peer->conn;
	return  conn->on_message(conn->session,msgFrame);
}

static int32_t bPer_pc_sendRtcMessage(BPerPeer* peer, BPerRtcMessageType mess){
	if(peer==NULL||peer->conn==NULL) return  ERROR_RTC_PEERCONNECTION;
	BPerRtcConnection *conn = (BPerRtcConnection*) peer->conn;
	return conn->notify(conn->session,mess);
}


int32_t bPer_pc_isAlive(BPerPeer* peer){
	if(peer==NULL||peer->conn==NULL) return BPer_Ok;
	BPerRtcConnection *conn = (BPerRtcConnection*) peer->conn;
	return conn->isAlive(conn->session);
}

static BPerRtcConnectionState bPer_pc_getConnectionState(BPerPeer* peer){
	if(peer==NULL || peer->conn==NULL) return BPer_Conn_State_New;
	BPerRtcConnection *conn = (BPerRtcConnection*) peer->conn;
	return conn->session->context.state;
}

 void bPer_pc_parseHeader(BPerVideoCodec codec,uint8_t *buf, uint8_t *src, int32_t *hLen){
	if(codec==BPer_VED_H264) bPer_getH264RtmpHeader(buf,src,hLen);
	if(codec==BPer_VED_H265) bPer_getH265RtmpHeader(buf,src,hLen);
}

static int32_t bPer_pc_getAnswerSdp(BPerPeer* peer,char* answer){
	if(peer==NULL||peer->conn==NULL) return ERROR_RTC_PEERCONNECTION;
	BPerRtcConnection *conn = (BPerRtcConnection*) peer->conn;

	return conn->createAnswer(conn->session,answer);
}
static int32_t bPer_pc_gethttpanswersdp(BPerPeer* peer,char* answer){
	if(peer==NULL||peer->conn==NULL) return ERROR_RTC_PEERCONNECTION;
	BPerRtcConnection *conn = (BPerRtcConnection*) peer->conn;

	return conn->createHttpAnswer(conn->session,answer);
}
 int32_t bPer_pc_getAnswerSdp2(BPerPeer* peer,char* answer){
	if(peer==NULL||peer->conn==NULL) return ERROR_RTC_PEERCONNECTION;
	BPerRtcConnection *conn = (BPerRtcConnection*) peer->conn;

	return conn->createHttpAnswer(conn->session,answer);
}

static int32_t bPer_pc_createOffer(BPerPeer* peer,char **psdp){
	if(peer==NULL||peer->conn==NULL) return ERROR_RTC_PEERCONNECTION;
	BPerRtcConnection *conn = (BPerRtcConnection*) peer->conn;
	return conn->createOffer(conn->session,psdp);

}



static BPerIceCandidateType bPer_pc_getIceCandidateType(BPerPeer* peer){
	if(peer==NULL||peer->conn==NULL) return BPerIceHost;
	BPerRtcConnection *conn = (BPerRtcConnection*) peer->conn;
	return conn->session->ice.session.candidateType;
}

void bPer_create_peerConnection(BPerPeerConnection* peerconn){
	if(peerconn==NULL) return;
	peerconn->peer.conn=NULL;
	peerconn->init=bPer_pc_init;
	peerconn->addAudioTrack=bPer_pc_addAudioTrack;
	peerconn->addVideoTrack=bPer_pc_addVideoTrack;
	peerconn->addTransceiver=bPer_pc_addTransceiver;
	peerconn->getIceCandidateType=bPer_pc_getIceCandidateType;

	peerconn->connectSfuServer=bPer_pc_connectServer;
	peerconn->connectWhipWhepServer=bPer_pc_connectWhipWhepServer;

	peerconn->createOffer=bPer_pc_createOffer;
	peerconn->createAnswer=bPer_pc_getAnswerSdp;
	peerconn->createHttpAnswer=bPer_pc_gethttpanswersdp;
	peerconn->setLocalDescription=bPer_pc_setLocalDescription;
	peerconn->setRemoteDescription=bPer_pc_setRemoteDescription;
	peerconn->close=bPer_pc_stopRtc;

	peerconn->on_audio=bPer_pc_onAudio;
	peerconn->on_video=bPer_pc_onVideo;
	peerconn->on_message=bPer_pc_onMessage;
	peerconn->isConnected=bPer_pc_isConnected;
	peerconn->isAlive=bPer_pc_isAlive;
	peerconn->getConnectionState=bPer_pc_getConnectionState;
	peerconn->sendRtcMessage=bPer_pc_sendRtcMessage;
	peerconn->createDataChannel=bPer_createDataChannel;
	bPer_pc_init(&peerconn->peer);
}

void bPer_destroy_peerConnection(BPerPeerConnection* peerconn){
	if(peerconn==NULL||peerconn->peer.conn==NULL) return;
	bPer_pc_stopRtc(&peerconn->peer);

}

